// RMSTransPassword.cpp: implementation of the CRMSTransPassword class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "RMSTransPassword.h"
#include "..\RMSLib\info.h"
#include "../RMSServer/RMSBaseServer.h"


#define MAX_CONNECT_RETRY 0x10


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CRMSTransPassword::CRMSTransPassword(
		CBaseServer *srv,char *Caller, CInfo& info, int Tag)
{

	PassStatus = 0;
	pBaseServer=srv;
	CameraInfo = info;
	strcpy(ServerIP,pBaseServer->GetProperIPForCamera(CameraInfo.m_ip));
	strcpy(SpyPassword, Caller);
	SaveTag = Tag;
	m_oriAddr = CameraInfo.getAddress();
	m_retAddr = 0;
}

CRMSTransPassword::~CRMSTransPassword()
{

}


BOOL CRMSTransPassword::Execute()
{
	return CRMSThread::Execute();
}

void Write2Log1(char* str,char* str2);
void Write2Log2(char* str,int a);
void Write2Log3(char* str,int a);


DWORD CRMSTransPassword::ThreadFunc()
{
	TRACE1("%x - CRMSTransPassword::ThreadFunc() - started\n", this);
	LINGER linger = { 1,0 };
	int i, retries;
	int err;
	bool binded;
	int Retries=11;
	static int sin_port=0;
	BOOL bReuse=TRUE;// reset port/address
	
	if (sin_port==0) sin_port=htons(pBaseServer->BasePort+200);

	if ((ntohs(sin_port)-pBaseServer->BasePort)>300) {
		sin_port=htons(pBaseServer->BasePort);
	}

	SetThreadPriority(m_hThread,THREAD_PRIORITY_ABOVE_NORMAL);




open:
	Retries--;
	Write2Log2("PasswordCheck-retries=",Retries);
	Write2Log1("Caller's ID:",SpyPassword);
	
	if (Retries<=0) goto exit;
	if((m_tcpSocket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET)
	{
		TRACE0("Unable to create socket.\n");
		goto exit;
	}
	//Fixes Bug in MFC Socket Impl
	setsockopt(m_tcpSocket,SOL_SOCKET,SO_LINGER,(LPCTSTR)&linger,sizeof(linger));
//	setsockopt(m_tcpSocket, SOL_SOCKET, SO_REUSEADDR,(LPCTSTR)&bReuse,sizeof(BOOL));

	memset (&m_saServer, 0, sizeof(SOCKADDR_IN)); 
	m_saServer.sin_family = PF_INET;
	m_saServer.sin_port = sin_port;

	m_saServer.sin_addr.s_addr = inet_addr(ServerIP);
	Write2Log1("m_saServer.sin_addr.s_addr =",ServerIP);

	memset (&m_saCamera, 0, sizeof(SOCKADDR_IN));
	m_saCamera.sin_family = PF_INET;
	m_saCamera.sin_addr.s_addr = m_oriAddr;
	m_saCamera.sin_port = htons(CameraInfo.m_port);

	Write2Log2("Setting up connection to cam ID=",CameraInfo.m_iID);
	Write2Log2("port=",ntohs(m_saCamera.sin_port));
	Write2Log1("IP=",static_cast<char*>(CameraInfo.m_ip));
	
	// find and bin local port to bind to
	binded = false;
	for (i=0;i<100;i++) {
		if (m_bQuit)
		{
			goto exit;
		}
		if(bind(m_tcpSocket,(sockaddr*)&m_saServer,sizeof(SOCKADDR_IN)) != INVALID_SOCKET) 
		{
			binded = true;
			break;
		}
		else
		{
			m_saServer.sin_port=htons(ntohs(m_saServer.sin_port)+1);
		}
	}

	Write2Log2("Selected port on server",ntohs(m_saServer.sin_port));

	if(!binded)
	{
		TRACE0("Unable to bind socket.\n");
		Write2Log1("Unable to bind socket","");
		goto exit;
	}
	sin_port=htons(ntohs(m_saServer.sin_port)+1);//get next to rotate ports

	err = 0;
	Write2Log1("Trying to connect...","");
	for(retries = 0; retries < MAX_CONNECT_RETRY; retries++)
	{
		if (m_bQuit)
		{
			goto exit;
		}
		if((err = connect(m_tcpSocket,(sockaddr*)&m_saCamera,sizeof(SOCKADDR_IN))) != SOCKET_ERROR)
		{
			break;
		}
	}



	if(err == SOCKET_ERROR)
	{
		Write2Log2("Unable to connect to server, m_bQuit=",m_bQuit);
		if (m_bQuit) 
		{
		goto exit;
		}
		TRACE0("Unable to connect to server.\n");
		goto exitError;
	}



	//_____________________________________________________ Check Password:

	if (SaveTag==TAG_PASSWORD_v01)
	{
		DWORD data[256];
		DWORD* Request;
		DWORD dwLen;
		DWORD dwTag;

		Write2Log1(">>>>>>>>>>>>>>Checking password","");
		Request = (DWORD*)data;
		DWORD* requestStart=Request;
		int	  tagCount=0;
		int nLenPass=strlen(SpyPassword)+1;
		
		*Request++ = TAG_REQUEST;
		*Request++ = 0;

		*Request++ = TAG_AUTHREQ;tagCount++;
		*Request++ = 0;


		*Request++ = TAG_RMSVIDEO_v01;tagCount++;
		*Request++ = 0;


		*Request++ = TAG_CAMERA_v01;tagCount++;
		*Request++ = sizeof(DWORD);
		*Request++ = CameraInfo.m_iID;
		

		*Request++ = TAG_PASSWORD_v01;tagCount++;
		*Request++ = nLenPass;
		strcpy(reinterpret_cast<char*>(Request), SpyPassword);		
		
		requestStart[1]=tagCount;
		dwLen=(Request-requestStart)*sizeof(DWORD)+nLenPass;
		Request = data;	
		
		if(send(m_tcpSocket,(char*)Request,dwLen,0) == SOCKET_ERROR)
		{
			goto exit;
		}

		if(RecvData(m_tcpSocket, m_bQuit, &dwTag, sizeof(DWORD))<0)
		{
			goto exit;
		}
		Write2Log3("Response:",dwTag);
		if(dwTag != TAG_ANSWER)
		{
			goto exit;
		}

		if(RecvData(m_tcpSocket, m_bQuit, &dwTag, sizeof(DWORD))<0)
		{
			goto exit;
		}
		// check if it's new server or encoder with ring support
		if (dwTag==0) {
			dwTag = TAG_GOODFORMAT;
			Write2Log1("Response=0"," possibly old version!");
			shutdown(m_tcpSocket,SD_BOTH);
			closesocket(m_tcpSocket);
			m_tcpSocket=0;
			PassStatus = 2;
			Write2Log1("Password exit","OK");
			TRACE2("%x - CRMSTransPassword::ThreadFunc() - finished, PassStatus=%d\n", this, PassStatus);
			return 1;
		}
		Write2Log3("Response:",dwTag);
		if (dwTag==TAG_SUPPORT_RING) {
			if(RecvData(m_tcpSocket, m_bQuit, &dwTag, sizeof(DWORD))<0)
			{
				goto exit;
			}
		}
		if(dwTag == TAG_GOODFORMAT)
		{
			shutdown(m_tcpSocket,SD_BOTH);
			closesocket(m_tcpSocket);
			m_tcpSocket=0;
			PassStatus = 1;

			Write2Log1("Password exit","OK");
			TRACE2("%x - CRMSTransPassword::ThreadFunc() - finished, PassStatus=%d\n", this, PassStatus);
			return 1;
		}

	} else {
	//____________________________________________________________Check Ring:

		DWORD data[256];
		DWORD* Request;
		DWORD dwLen;
		DWORD dwTag;

		Request = (DWORD*)data;
		DWORD* requestStart=Request;
		int	  tagCount=0;
		int nLenPass=strlen(SpyPassword)+1;
		
		Write2Log1(">>>>>>>>>>>>>>Checking for caller's ID","");
		*Request++ = TAG_REQUEST;
		*Request++ = 0;

		*Request++ = TAG_AUTHREQ;tagCount++;
		*Request++ = 0;
		

		*Request++ = TAG_RMSVIDEO_v01;tagCount++;
		*Request++ = 0;

		*Request++ = TAG_CAMERA_v01;tagCount++;
		*Request++ = sizeof(DWORD);
		*Request++ = CameraInfo.m_iID;



		*Request++ = TAG_VC_RING;tagCount++;
		*Request++ = nLenPass;
		strcpy(reinterpret_cast<char*>(Request), SpyPassword);		
		
		requestStart[1]=tagCount;
		dwLen=(Request-requestStart)*sizeof(DWORD)+nLenPass;
		Request = data;	
		
		if(send(m_tcpSocket,(char*)Request,dwLen,0) == SOCKET_ERROR)
		{
			goto exitError;
		}

		dwTag=TAG_CONFWAIT;
		while (dwTag==TAG_CONFWAIT) {
			if(RecvData(m_tcpSocket, m_bQuit, &dwTag, sizeof(DWORD))<0)
			{
				goto exitError;
			}
			Sleep(100);
		}
		Write2Log3("Response:",dwTag);
		if(dwTag != TAG_ANSWER)
		{
			goto exitError;
		}

		DWORD stat = 0L;
		if(RecvData(m_tcpSocket, m_bQuit, &stat, sizeof(DWORD))<0)
		{
			goto exitError;
		}

		// try to get returned address: tag + address
		DWORD answ[4];
		if(RecvData(m_tcpSocket, m_bQuit, &answ, sizeof(answ)) > 0)
		{
//			if( (answ[0] == 1) &&
			if( (answ[0] == -1) &&
				(answ[1] == TAG_ADDRESS_ON_RING) &&
				(answ[2] == sizeof(DWORD)))
			{
				answ[3] ^= (DWORD)CInfo::KEY;
				if(answ[3] != 0)
					m_retAddr = answ[3];
			}
		}

		PassStatus = stat;

		shutdown(m_tcpSocket,SD_BOTH);
		closesocket(m_tcpSocket);
		m_tcpSocket=0;
		Write2Log1("Ring exit","OK");
		TRACE2("%x - CRMSTransPassword::ThreadFunc() - finished, PassStatus=%d\n", this, PassStatus);
		return 1;
	}
	//____________________________________________________________
exit:	
	Write2Log1("Password exit","Bad");
	shutdown(m_tcpSocket,SD_BOTH);
	closesocket(m_tcpSocket);
	m_tcpSocket=0;
	PassStatus = -1;
	TRACE2("%x - CRMSTransPassword::ThreadFunc() - finished, PassStatus=%d\n", this, PassStatus);
	return 1;

exitError:
	Write2Log1("Password error-repeat","");
	closesocket(m_tcpSocket);
	goto open;
}

unsigned long CRMSTransPassword::getReturnedAddrIf()
{
	return m_retAddr;
//	return (m_retAddr != m_oriAddr) ? m_retAddr : 0;
}

int CRMSTransPassword::RecvData(SOCKET& Socket,BOOL& bTerminated, void* data, int Len)
{

	int i=0;
	while (i<Len) {
		if(bTerminated) return -1;
		int n = recv(Socket,(char*)data+i, Len-i, 0);
		if (n == SOCKET_ERROR) {
				return -1;
		}
		if (n == 0) {
 				return -1;
		}
		i+=n;
	}
	return i;


}
